#include "ink_screen.h"
#include <string.h>

// ink screen 刷新模式：全刷、局部刷、快速刷
typedef enum 
{
    INK_SCREEN_REFRESH_FULL = 0,
    INK_SCREEN_REFRESH_PARTIAL = 1,
    INK_SCREEN_REFRESH_FAST = 2,
}ink_screen_refresh_mode_t;

// 显示器显存
static uint8_t ink_screen_display_memory_[(INK_SCREEN_WIDTH + 7) / 8 * INK_SCREEN_HEIGHT];
static uint16_t ink_screen_display_memory_size_ = (INK_SCREEN_WIDTH + 7) / 8 * INK_SCREEN_HEIGHT;
static ink_screen_refresh_mode_t ink_screen_refresh_mode_ = INK_SCREEN_REFRESH_FULL;
// 局部刷新次数记录
static uint8_t ink_screen_partial_refresh_count_ = 0;
// 局部刷新最多次数 默认8次
static uint8_t ink_screen_partial_refresh_max_count_ = 8;
// ink screen 背景颜色 默认白色
static ink_screen_color_t ink_screen_background_color_ = INK_SCREEN_COLOR_WHITE;
// 是否为快速刷新
static bool ink_screen_fast_refresh_ = false;

// 函数声明
static void ink_screen_reset_(void);
static void ink_screen_send_command_(uint8_t reg);
static void ink_screen_send_data_(uint8_t data);
static void ink_screen_send_datas_(uint8_t *datas, uint32_t len);
static void ink_screen_wait_idle_(void);
static void ink_screen_set_windows_(uint16_t x_start, uint16_t y_start, uint16_t x_end, uint16_t y_end);
static void ink_screen_set_cursor(uint16_t Xstart, uint16_t Ystart);
static void ink_screen_turn_on_display_full_(void);
static void ink_screen_turn_on_display_fast_(void);
static void ink_screen_turn_on_display_partial_(void);
static void ink_screen_set_refresh_mode_(ink_screen_refresh_mode_t mode);
static void ink_screen_init_partial_(void);
static void ink_screen_init_full_(void);
static void ink_screen_init_fast_(void);


/**
 * @brief 重置墨水屏
 */
static void ink_screen_reset_(void)
{
    ink_screen_digital_write(EPD_RST_PIN, 1);
    ink_screen_delay_ms(20);
    ink_screen_digital_write(EPD_RST_PIN, 0);
    ink_screen_delay_ms(2);
    ink_screen_digital_write(EPD_RST_PIN, 1);
    ink_screen_delay_ms(20);
}

/**
 * @brief 发送命令到墨水屏
 *
 * @param reg 命令寄存器
 */
static void ink_screen_send_command_(uint8_t reg)
{
    ink_screen_digital_write(EPD_DC_PIN, 0);
    ink_screen_digital_write(EPD_CS_PIN, 0);
    ink_screen_spi_write_bytes(&reg, 1);
    ink_screen_digital_write(EPD_CS_PIN, 1);
}

/**
 * @brief 发送数据到墨水屏
 *
 * @param data 数据
 */
static void ink_screen_send_data_(uint8_t data)
{
    ink_screen_send_datas_(&data, 1);
}

/**
 * @brief 发送多个数据到墨水屏
 *
 * @param datas 数据数组
 * @param len 数据长度
 */
static void ink_screen_send_datas_(uint8_t *datas, uint32_t len)
{
    ink_screen_digital_write(EPD_DC_PIN, 1);
    ink_screen_digital_write(EPD_CS_PIN, 0);
    ink_screen_spi_write_bytes(datas, len);
    ink_screen_digital_write(EPD_CS_PIN, 1);
}

/**
 * @brief 等待墨水屏空闲
 */
static void ink_screen_wait_idle_(void)
{
    while (ink_screen_digital_read(EPD_BUSY_PIN))
    {
        ink_screen_delay_ms(10);
    }
    ink_screen_delay_ms(10);
}

/**
 * 设置墨水屏的窗口范围
 *
 * @param x_start 窗口的起始X坐标
 * @param y_start 窗口的起始Y坐标
 * @param x_end 窗口的结束X坐标
 * @param y_end 窗口的结束Y坐标
 */
static void ink_screen_set_windows_(uint16_t x_start, uint16_t y_start, uint16_t x_end, uint16_t y_end)
{
    ink_screen_send_command_(0x44); // 设置RAM X地址的起始和结束位置
    ink_screen_send_data_((x_start >> 3) & 0xFF);
    ink_screen_send_data_((x_end >> 3) & 0xFF);

    ink_screen_send_command_(0x45); // 设置RAM Y地址的起始和结束位置
    ink_screen_send_data_(y_start & 0xFF);
    ink_screen_send_data_((y_start >> 8) & 0xFF);
    ink_screen_send_data_(y_end & 0xFF);
    ink_screen_send_data_((y_end >> 8) & 0xFF);
}

/**
 * 在墨水屏上设置光标位置。
 *
 * @param Xstart 光标位置的X坐标。
 * @param Ystart 光标位置的Y坐标。
 */
static void ink_screen_set_cursor(uint16_t Xstart, uint16_t Ystart)
{
    ink_screen_send_command_(0x4E); // 设置RAM的X地址计数器
    ink_screen_send_data_(Xstart & 0xFF);

    ink_screen_send_command_(0x4F); // 设置RAM的Y地址计数器
    ink_screen_send_data_(Ystart & 0xFF);
    ink_screen_send_data_((Ystart >> 8) & 0xFF);
}

/**
 * @brief 打开显示屏
 *
 * 打开显示屏并进行显示更新控制。
 *
 * @note 此函数将发送命令和数据以控制显示屏的更新。
 */
static void ink_screen_turn_on_display_full_(void)
{
    ink_screen_send_command_(0x22); // 显示更新控制
    ink_screen_send_data_(0xf7);
    ink_screen_send_command_(0x20); // 激活显示更新序列
    ink_screen_wait_idle_();
}

/**
 * @brief 快速打开显示屏
 *
 * 快速打开显示屏并进行显示更新控制。
 *
 * @note 此函数将发送命令和数据以控制显示屏的更新。
 */
static void ink_screen_turn_on_display_fast_(void)
{
    ink_screen_send_command_(0x22); // 显示更新控制
    ink_screen_send_data_(0xc7);    // 快速:0x0c, 质量:0x0f, 0xcf
    ink_screen_send_command_(0x20); // 激活显示更新序列
    ink_screen_wait_idle_();
}

/**
 * @brief 部分打开显示屏
 *
 * 部分打开显示屏并进行显示更新控制。
 *
 * @note 此函数将发送命令和数据以控制显示屏的更新。
 */
static void ink_screen_turn_on_display_partial_(void)
{
    ink_screen_send_command_(0x22); // 显示更新控制
    ink_screen_send_data_(0xff);    // 快速:0x0c, 质量:0x0f, 0xcf
    ink_screen_send_command_(0x20); // 激活显示更新序列
    ink_screen_wait_idle_();
}


/**
 * @brief 打开显示屏
 *
 * 打开显示屏并进行显示更新控制。
 *
 * @note 此函数将发送命令和数据以控制显示屏的更新。
 */
static void ink_screen_turn_on_display_(void)
{
    switch (ink_screen_refresh_mode_)
    {
    case INK_SCREEN_REFRESH_FULL:
        ink_screen_turn_on_display_full_();
        ink_screen_init_partial_();
        break;
    case INK_SCREEN_REFRESH_PARTIAL:
        ink_screen_turn_on_display_partial_();
        if (++ink_screen_partial_refresh_count_ == ink_screen_partial_refresh_max_count_)
        {
            ink_screen_partial_refresh_count_ = 0;
            if (ink_screen_fast_refresh_)
                ink_screen_init_fast_();
            else
                ink_screen_init_full_();
        }
        break;
    case INK_SCREEN_REFRESH_FAST:
        ink_screen_turn_on_display_fast_();
        ink_screen_init_partial_();
        break;
    default:
        break;
    }
}

/**
 * @brief 设置墨水屏刷新模式
 *
 * @param mode 刷新模式
 */
static void ink_screen_set_refresh_mode_(ink_screen_refresh_mode_t mode)
{
    ink_screen_refresh_mode_ = mode;
}


static void ink_screen_init_partial_(void)
{
    ink_screen_set_refresh_mode_(INK_SCREEN_REFRESH_PARTIAL);
    // 复位墨水屏
    ink_screen_reset_();
    ink_screen_wait_idle_(); // 等待墨水屏空闲
    // 发送命令，设置边框波形
    ink_screen_send_command_(0x3C);
    ink_screen_send_data_(0x80);

    // 发送命令，设置驱动输出控制
    ink_screen_send_command_(0x01);
    ink_screen_send_data_(0xF9);
    ink_screen_send_data_(0x00);
    ink_screen_send_data_(0x00);

    // 发送命令，设置数据输入模式
    ink_screen_send_command_(0x11);
    ink_screen_send_data_(0x03);

    // 设置窗口和光标
    ink_screen_set_windows_(0, 0, INK_SCREEN_WIDTH - 1, INK_SCREEN_HEIGHT - 1);
    ink_screen_set_cursor(0, 0);

    // 部分打开墨水屏显示
    ink_screen_turn_on_display_partial_();
}

static void ink_screen_init_full_(void)
{
    ink_screen_set_refresh_mode_(INK_SCREEN_REFRESH_FULL);
    ink_screen_fast_refresh_ = false;

    ink_screen_reset_();            // 复位墨水屏
    ink_screen_send_command_(0x12); // 发送SWRESET命令，软复位墨水屏
    ink_screen_wait_idle_();        // 等待墨水屏空闲

    ink_screen_send_command_(0x01); // 发送Driver output control命令
    ink_screen_send_data_(0xF9);    // 设置Driver output control的参数
    ink_screen_send_data_(0x00);
    ink_screen_send_data_(0x00);

    ink_screen_send_command_(0x11); // 发送data entry mode命令
    ink_screen_send_data_(0x03);    // 设置data entry mode的参数

    ink_screen_set_windows_(0, 0, INK_SCREEN_WIDTH - 1, INK_SCREEN_HEIGHT - 1); // 设置墨水屏的窗口范围
    ink_screen_set_cursor(0, 0);                                                // 设置墨水屏的光标位置

    ink_screen_send_command_(0x3C); // 发送BorderWavefrom命令
    ink_screen_send_data_(0x05);    // 设置BorderWavefrom的参数

    ink_screen_send_command_(0x21); // 发送Display update control命令
    ink_screen_send_data_(0x00);    // 设置Display update control的参数
    ink_screen_send_data_(0x80);

    ink_screen_send_command_(0x18); // 发送Read built-in temperature sensor命令
    ink_screen_send_data_(0x80);    // 设置Read built-in temperature sensor的参数
    ink_screen_wait_idle_();        // 等待墨水屏空闲
}

static void ink_screen_init_fast_(void)
{
    ink_screen_set_refresh_mode_(INK_SCREEN_REFRESH_FAST);
    ink_screen_fast_refresh_ = true;

    ink_screen_reset_();            // 复位墨水屏
    ink_screen_send_command_(0x12); // 发送SWRESET命令，软复位墨水屏
    ink_screen_wait_idle_();        // 等待墨水屏空闲

    ink_screen_send_command_(0x18); // 发送Read built-in temperature sensor命令
    ink_screen_send_data_(0x80);    // 设置Read built-in temperature sensor的参数

    ink_screen_send_command_(0x11); // 发送data entry mode命令
    ink_screen_send_data_(0x03);    // 设置data entry mode的参数

    ink_screen_set_windows_(0, 0, INK_SCREEN_WIDTH - 1, INK_SCREEN_HEIGHT - 1); // 设置墨水屏的窗口范围
    ink_screen_set_cursor(0, 0);                                                // 设置墨水屏的光标位置

    ink_screen_send_command_(0x22); // 发送Load temperature value命令
    ink_screen_send_data_(0xB1);    // 设置Load temperature value的参数
    ink_screen_send_command_(0x20);
    ink_screen_wait_idle_(); // 等待墨水屏空闲

    ink_screen_send_command_(0x1A); // 发送Write to temperature register命令
    ink_screen_send_data_(0x64);    // 设置Write to temperature register的参数
    ink_screen_send_data_(0x00);

    ink_screen_send_command_(0x22); // 发送Load temperature value命令
    ink_screen_send_data_(0x91);    // 设置Load temperature value的参数
    ink_screen_send_command_(0x20);
    ink_screen_wait_idle_(); // 等待墨水屏空闲
}

/**
 * @brief 初始化墨水屏。
 */
void ink_screen_init(ink_screen_init_mode_t mode)
{

    ink_screen_gpio_mode(EPD_CS_PIN, INK_SCREEN_GPIO_MODE_OUTPUT);
    ink_screen_gpio_mode(EPD_RST_PIN, INK_SCREEN_GPIO_MODE_OUTPUT);
    ink_screen_gpio_mode(EPD_DC_PIN, INK_SCREEN_GPIO_MODE_OUTPUT);
    ink_screen_gpio_mode(EPD_BUSY_PIN, INK_SCREEN_GPIO_MODE_INPUT);

    ink_screen_spi_init();

    switch (mode)
    {
    case INK_SCREEN_INIT_MODE_NORMAL:
        ink_screen_init_full_();
        break;
    case INK_SCREEN_INIT_MODE_FAST:
        ink_screen_init_fast_();
        break;
    default:
        break;
    }
}

/**
 * @brief 清除墨水屏上的内容，将整个屏幕填充为白色。
 */
void ink_screen_clear_write(void)
{
    memset(ink_screen_display_memory_, 0xFF, ink_screen_display_memory_size_);
    ink_screen_refresh();
}

/**
 * @brief 清除墨水屏上的内容，将整个屏幕填充为黑色。
 */
void ink_screen_clear_black(void)
{
    memset(ink_screen_display_memory_, 0x00, ink_screen_display_memory_size_);
    ink_screen_refresh();
}


/**
 * @brief 设置墨水屏的背景颜色。
 * 
 * @param color 背景颜色
 */
void ink_screen_set_background_color(ink_screen_color_t color)
{
    ink_screen_background_color_ = color;
}

/**
 * @brief 清除墨水屏上的内容为背景色。
 */
void ink_screen_clear(void)
{
    if (ink_screen_background_color_ == INK_SCREEN_COLOR_BLACK)
    {
        ink_screen_clear_black();
    }
    else
    {
        ink_screen_clear_write();
    }
}

/**
 * @brief 刷新墨水屏上的内容。
 */
void ink_screen_refresh(void)
{
    // 发送命令，设置写入数据模式为黑色
    ink_screen_send_command_(0x24);
    ink_screen_send_datas_(ink_screen_display_memory_, ink_screen_display_memory_size_);
    // 打开墨水屏显示
    ink_screen_turn_on_display_();
}


/**
 * @brief 将墨水屏进入休眠状态。
 */
void ink_screen_sleep(void)
{
    // 发送命令，进入深度休眠
    ink_screen_send_command_(0x10);
    ink_screen_send_data_(0x01);
    ink_screen_delay_ms(100);
}

/**
 * @brief 设置部分刷新最大次数
 */
void ink_screen_set_partial_refresh_max_count(uint8_t count)
{
    ink_screen_partial_refresh_max_count_ = count;
}